#ifndef TB_PREFIX_ASSERT_H
#define TB_PREFIX_ASSERT_H

#include "config.h"
#include "trace.h"
#include "abort.h"
#include "check.h"
#include "keyword.h"

__tb_extern_c_enter__

/* //////////////////////////////////////////////////////////////////////////////////////
 * macros
 */

// the assert backtrace prefix
#define TB_ASSERT_BACKTRACE_PREFIX "    "

// the assert backtrace nframe
#define TB_ASSERT_BACKTRACE_NFRAME (16)

// the assert backtrace dump
#define tb_assert_backtrace_dump()                                             \
    tb_backtrace_dump(                                                         \
        TB_ASSERT_BACKTRACE_PREFIX, tb_null, TB_ASSERT_BACKTRACE_NFRAME)

// assert
#ifdef __tb_debug__
#if defined(TB_COMPILER_IS_GCC) || defined(TB_COMPILER_IS_TINYC)
#define tb_assertf(x, fmt, arg...)                                             \
    do {                                                                       \
        if (!(x)) {                                                            \
            tb_trace_a("expr[%s]: " fmt, #x, ##arg);                           \
            tb_assert_backtrace_dump();                                        \
            tb_trace_sync();                                                   \
            tb_abort();                                                        \
        }                                                                      \
    } while (0)
#define tb_assertf_and_check_abort(x, fmt, arg...) tb_assertf(x, fmt, ##arg)
#define tb_assertf_and_check_return(x, fmt, arg...) tb_assertf(x, fmt, ##arg)
#define tb_assertf_and_check_return_val(x, v, fmt, arg...)                     \
    tb_assertf(x, fmt, ##arg)
#define tb_assertf_and_check_goto(x, b, fmt, arg...) tb_assertf(x, fmt, ##arg)
#define tb_assertf_and_check_break(x, fmt, arg...) tb_assertf(x, fmt, ##arg)
#define tb_assertf_and_check_continue(x, fmt, arg...) tb_assertf(x, fmt, ##arg)
#elif defined(TB_COMPILER_IS_MSVC) && TB_COMPILER_VERSION_BE(13, 0)
#define tb_assertf(x, fmt, ...)                                                \
    do {                                                                       \
        if (!(x)) {                                                            \
            tb_trace_a("expr[%s]: " fmt, #x, __VA_ARGS__);                     \
            tb_assert_backtrace_dump();                                        \
            tb_trace_sync();                                                   \
            tb_abort();                                                        \
        }                                                                      \
    } while (0)
#define tb_assertf_and_check_abort(x, fmt, ...) tb_assertf(x, fmt, __VA_ARGS__)
#define tb_assertf_and_check_return(x, fmt, ...) tb_assertf(x, fmt, __VA_ARGS__)
#define tb_assertf_and_check_return_val(x, v, fmt, ...)                        \
    tb_assertf(x, fmt, __VA_ARGS__)
#define tb_assertf_and_check_goto(x, b, fmt, ...)                              \
    tb_assertf(x, fmt, __VA_ARGS__)
#define tb_assertf_and_check_break(x, fmt, ...) tb_assertf(x, fmt, __VA_ARGS__)
#define tb_assertf_and_check_continue(x, fmt, ...)                             \
    tb_assertf(x, fmt, __VA_ARGS__)
#else
#define tb_assertf
#define tb_assertf_and_check_abort
#define tb_assertf_and_check_return
#define tb_assertf_and_check_return_val
#define tb_assertf_and_check_goto
#define tb_assertf_and_check_break
#define tb_assertf_and_check_continue
#endif
#else
#if defined(TB_COMPILER_IS_GCC) || defined(TB_COMPILER_IS_TINYC)
#define tb_assertf(x, fmt, arg...)
#define tb_assertf_and_check_abort(x, fmt, arg...) tb_check_abort(x)
#define tb_assertf_and_check_return(x, fmt, arg...) tb_check_return(x)
#define tb_assertf_and_check_return_val(x, v, fmt, arg...)                     \
    tb_check_return_val(x, v)
#define tb_assertf_and_check_goto(x, b, fmt, arg...) tb_check_goto(x, b)
#define tb_assertf_and_check_break(x, fmt, arg...) tb_check_break(x)
#define tb_assertf_and_check_continue(x, fmt, arg...) tb_check_continue(x)
#elif defined(TB_COMPILER_IS_MSVC) && TB_COMPILER_VERSION_BE(13, 0)
#define tb_assertf(x, fmt, ...)
#define tb_assertf_and_check_abort(x, fmt, ...) tb_check_abort(x)
#define tb_assertf_and_check_return(x, fmt, ...) tb_check_return(x)
#define tb_assertf_and_check_return_val(x, v, fmt, ...)                        \
    tb_check_return_val(x, v)
#define tb_assertf_and_check_goto(x, b, fmt, ...) tb_check_goto(x, b)
#define tb_assertf_and_check_break(x, fmt, ...) tb_check_break(x)
#define tb_assertf_and_check_continue(x, fmt, ...) tb_check_continue(x)
#else
#define tb_assertf
#define tb_assertf_and_check_abort
#define tb_assertf_and_check_return
#define tb_assertf_and_check_return_val
#define tb_assertf_and_check_goto
#define tb_assertf_and_check_break
#define tb_assertf_and_check_continue
#endif
#endif

#ifdef __tb_debug__
#define tb_assert(x) tb_assert_impl(!!(x), #x __tb_debug_vals__)
#define tb_assert_and_check_abort(x) tb_assert(x)
#define tb_assert_and_check_return(x) tb_assert(x)
#define tb_assert_and_check_return_val(x, v) tb_assert(x)
#define tb_assert_and_check_goto(x, b) tb_assert(x)
#define tb_assert_and_check_break(x) tb_assert(x)
#define tb_assert_and_check_continue(x) tb_assert(x)
#define tb_assert_and_check_break_state(x, s, v) tb_assert(x)
#else
#define tb_assert(x) tb_assert_empty_impl()
#define tb_assert_and_check_abort(x) tb_check_abort(x)
#define tb_assert_and_check_return(x) tb_check_return(x)
#define tb_assert_and_check_return_val(x, v) tb_check_return_val(x, v)
#define tb_assert_and_check_goto(x, b) tb_check_goto(x, b)
#define tb_assert_and_check_break(x) tb_check_break(x)
#define tb_assert_and_check_continue(x) tb_check_continue(x)
#define tb_assert_and_check_break_state(x, s, v) tb_check_break_state(x, s, v)
#endif

// assert and pass code, not abort it
#ifdef __tb_debug__
#if defined(TB_COMPILER_IS_GCC) || defined(TB_COMPILER_IS_TINYC)
#define tb_assertf_pass_return(x, fmt, arg...)                                 \
    do {                                                                       \
        if (!(x)) {                                                            \
            tb_trace_a("expr[%s]: " fmt, #x, ##arg);                           \
            tb_assert_backtrace_dump();                                        \
            tb_trace_sync();                                                   \
            return;                                                            \
        }                                                                      \
    } while (0)
#define tb_assertf_pass_return_val(x, v, fmt, arg...)                          \
    do {                                                                       \
        if (!(x)) {                                                            \
            tb_trace_a("expr[%s]: " fmt, #x, ##arg);                           \
            tb_assert_backtrace_dump();                                        \
            tb_trace_sync();                                                   \
            return (v);                                                        \
        }                                                                      \
    } while (0)
#define tb_assertf_pass_goto(x, b, fmt, arg...)                                \
    do {                                                                       \
        if (!(x)) {                                                            \
            tb_trace_a("expr[%s]: " fmt, #x, ##arg);                           \
            tb_assert_backtrace_dump();                                        \
            tb_trace_sync();                                                   \
            goto b;                                                            \
        }                                                                      \
    } while (0)
#define tb_assertf_pass_break(x, fmt, arg...)                                  \
    {                                                                          \
        if (!(x)) {                                                            \
            tb_trace_a("expr[%s]: " fmt, #x, ##arg);                           \
            tb_assert_backtrace_dump();                                        \
            tb_trace_sync();                                                   \
            break;                                                             \
        }                                                                      \
    }
#define tb_assertf_pass_continue(x, fmt, arg...)                               \
    {                                                                          \
        if (!(x)) {                                                            \
            tb_trace_a("expr[%s]: " fmt, #x, ##arg);                           \
            tb_assert_backtrace_dump();                                        \
            tb_trace_sync();                                                   \
            continue;                                                          \
        }                                                                      \
    }
#define tb_assertf_pass_and_check_abort(x, fmt, arg...)                        \
    tb_assertf_pass_abort(x, fmt, ##arg)
#define tb_assertf_pass_and_check_return(x, fmt, arg...)                       \
    tb_assertf_pass_return(x, fmt, ##arg)
#define tb_assertf_pass_and_check_return_val(x, v, fmt, arg...)                \
    tb_assertf_pass_return_val(x, v, fmt, ##arg)
#define tb_assertf_pass_and_check_goto(x, b, fmt, arg...)                      \
    tb_assertf_pass_goto(x, b, fmt, ##arg)
#define tb_assertf_pass_and_check_break(x, fmt, arg...)                        \
    tb_assertf_pass_break(x, fmt, ##arg)
#define tb_assertf_pass_and_check_continue(x, fmt, arg...)                     \
    tb_assertf_pass_continue(x, fmt, ##arg)
#elif defined(TB_COMPILER_IS_MSVC) && TB_COMPILER_VERSION_BE(13, 0)
#define tb_assertf_pass_return(x, fmt, ...)                                    \
    do {                                                                       \
        if (!(x)) {                                                            \
            tb_trace_a("expr[%s]: " fmt, #x, __VA_ARGS__);                     \
            tb_assert_backtrace_dump();                                        \
            tb_trace_sync();                                                   \
            return;                                                            \
        }                                                                      \
    } while (0)
#define tb_assertf_pass_return_val(x, v, fmt, ...)                             \
    do {                                                                       \
        if (!(x)) {                                                            \
            tb_trace_a("expr[%s]: " fmt, #x, __VA_ARGS__);                     \
            tb_assert_backtrace_dump();                                        \
            tb_trace_sync();                                                   \
            return (v);                                                        \
        }                                                                      \
    } while (0)
#define tb_assertf_pass_goto(x, b, fmt, ...)                                   \
    do {                                                                       \
        if (!(x)) {                                                            \
            tb_trace_a("expr[%s]: " fmt, #x, __VA_ARGS__);                     \
            tb_assert_backtrace_dump();                                        \
            tb_trace_sync();                                                   \
            goto b;                                                            \
        }                                                                      \
    } while (0)
#define tb_assertf_pass_break(x, fmt, ...)                                     \
    {                                                                          \
        if (!(x)) {                                                            \
            tb_trace_a("expr[%s]: " fmt, #x, __VA_ARGS__);                     \
            tb_assert_backtrace_dump();                                        \
            tb_trace_sync();                                                   \
            break;                                                             \
        }                                                                      \
    }
#define tb_assertf_pass_continue(x, fmt, ...)                                  \
    {                                                                          \
        if (!(x)) {                                                            \
            tb_trace_a("expr[%s]: " fmt, #x, __VA_ARGS__);                     \
            tb_assert_backtrace_dump();                                        \
            tb_trace_sync();                                                   \
            continue;                                                          \
        }                                                                      \
    }
#define tb_assertf_pass_and_check_abort(x, fmt, ...)                           \
    tb_assertf_pass_abort(x, fmt, __VA_ARGS__)
#define tb_assertf_pass_and_check_return(x, fmt, ...)                          \
    tb_assertf_pass_return(x, fmt, __VA_ARGS__)
#define tb_assertf_pass_and_check_return_val(x, v, fmt, ...)                   \
    tb_assertf_pass_return_val(x, v, fmt, __VA_ARGS__)
#define tb_assertf_pass_and_check_goto(x, b, fmt, ...)                         \
    tb_assertf_pass_goto(x, b, fmt, __VA_ARGS__)
#define tb_assertf_pass_and_check_break(x, fmt, ...)                           \
    tb_assertf_pass_break(x, fmt, __VA_ARGS__)
#define tb_assertf_pass_and_check_continue(x, fmt, ...)                        \
    tb_assertf_pass_continue(x, fmt, __VA_ARGS__)
#else
#define tb_assertf_pass_return
#define tb_assertf_pass_return_val
#define tb_assertf_pass_goto
#define tb_assertf_pass_break
#define tb_assertf_pass_continue
#define tb_assertf_pass_and_check_abort
#define tb_assertf_pass_and_check_return
#define tb_assertf_pass_and_check_return_val
#define tb_assertf_pass_and_check_goto
#define tb_assertf_pass_and_check_break
#define tb_assertf_pass_and_check_continue
#endif
#else
#if defined(TB_COMPILER_IS_GCC) || defined(TB_COMPILER_IS_TINYC)
#define tb_assertf_pass_return(x, fmt, arg...)
#define tb_assertf_pass_return_val(x, v, fmt, arg...)
#define tb_assertf_pass_goto(x, b, fmt, arg...)
#define tb_assertf_pass_break(x, fmt, arg...)
#define tb_assertf_pass_continue(x, fmt, arg...)
#define tb_assertf_pass_and_check_abort(x, fmt, arg...) tb_check_abort(x)
#define tb_assertf_pass_and_check_return(x, fmt, arg...) tb_check_return(x)
#define tb_assertf_pass_and_check_return_val(x, v, fmt, arg...)                \
    tb_check_return_val(x, v)
#define tb_assertf_pass_and_check_goto(x, b, fmt, arg...) tb_check_goto(x, b)
#define tb_assertf_pass_and_check_break(x, fmt, arg...) tb_check_break(x)
#define tb_assertf_pass_and_check_continue(x, fmt, arg...) tb_check_continue(x)
#elif defined(TB_COMPILER_IS_MSVC) && TB_COMPILER_VERSION_BE(13, 0)
#define tb_assertf_pass_return(x, fmt, ...)
#define tb_assertf_pass_return_val(x, v, fmt, ...)
#define tb_assertf_pass_goto(x, b, fmt, ...)
#define tb_assertf_pass_break(x, fmt, ...)
#define tb_assertf_pass_continue(x, fmt, ...)
#define tb_assertf_pass_and_check_abort(x, fmt, ...) tb_check_abort(x)
#define tb_assertf_pass_and_check_return(x, fmt, ...) tb_check_return(x)
#define tb_assertf_pass_and_check_return_val(x, v, fmt, ...)                   \
    tb_check_return_val(x, v)
#define tb_assertf_pass_and_check_goto(x, b, fmt, ...) tb_check_goto(x, b)
#define tb_assertf_pass_and_check_break(x, fmt, ...) tb_check_break(x)
#define tb_assertf_pass_and_check_continue(x, fmt, ...) tb_check_continue(x)
#else
#define tb_assertf_pass_return
#define tb_assertf_pass_return_val
#define tb_assertf_pass_goto
#define tb_assertf_pass_break
#define tb_assertf_pass_continue
#define tb_assertf_pass_and_check_abort
#define tb_assertf_pass_and_check_return
#define tb_assertf_pass_and_check_return_val
#define tb_assertf_pass_and_check_goto
#define tb_assertf_pass_and_check_break
#define tb_assertf_pass_and_check_continue
#endif
#endif

#ifdef __tb_debug__
#define tb_assert_pass_return(x)                                               \
    do {                                                                       \
        if (!(x)) {                                                            \
            tb_trace_a("expr[%s]", #x);                                        \
            tb_assert_backtrace_dump();                                        \
            tb_trace_sync();                                                   \
            return;                                                            \
        }                                                                      \
    } while (0)
#define tb_assert_pass_return_val(x, v)                                        \
    do {                                                                       \
        if (!(x)) {                                                            \
            tb_trace_a("expr[%s]", #x);                                        \
            tb_assert_backtrace_dump();                                        \
            tb_trace_sync();                                                   \
            return (v);                                                        \
        }                                                                      \
    } while (0)
#define tb_assert_pass_goto(x, b)                                              \
    do {                                                                       \
        if (!(x)) {                                                            \
            tb_trace_a("expr[%s]", #x);                                        \
            tb_assert_backtrace_dump();                                        \
            tb_trace_sync();                                                   \
            goto b;                                                            \
        }                                                                      \
    } while (0)
#define tb_assert_pass_break(x)                                                \
    {                                                                          \
        if (!(x)) {                                                            \
            tb_trace_a("expr[%s]", #x);                                        \
            tb_assert_backtrace_dump();                                        \
            tb_trace_sync();                                                   \
            break;                                                             \
        }                                                                      \
    }
#define tb_assert_pass_continue(x)                                             \
    {                                                                          \
        if (!(x)) {                                                            \
            tb_trace_a("expr[%s]", #x);                                        \
            tb_assert_backtrace_dump();                                        \
            tb_trace_sync();                                                   \
            continue;                                                          \
        }                                                                      \
    }
#define tb_assert_pass_break_state(x, s, v)                                    \
    {                                                                          \
        if (!(x)) {                                                            \
            (s) = (v);                                                         \
            tb_trace_a("expr[%s]", #x);                                        \
            tb_assert_backtrace_dump();                                        \
            tb_trace_sync();                                                   \
            break;                                                             \
        }                                                                      \
    }

#define tb_assert_pass_and_check_abort(x) tb_assert_pass_abort(x)
#define tb_assert_pass_and_check_return(x) tb_assert_pass_return(x)
#define tb_assert_pass_and_check_return_val(x, v)                              \
    tb_assert_pass_return_val(x, v)
#define tb_assert_pass_and_check_goto(x, b) tb_assert_pass_goto(x, b)
#define tb_assert_pass_and_check_break(x) tb_assert_pass_break(x)
#define tb_assert_pass_and_check_continue(x) tb_assert_pass_continue(x)
#define tb_assert_pass_and_check_break_state(x, s, v)                          \
    tb_assert_pass_break_state(x, s, v)
#else
#define tb_assert_pass_return(x)
#define tb_assert_pass_return_val(x, v)
#define tb_assert_pass_goto(x, b)
#define tb_assert_pass_break(x)
#define tb_assert_pass_continue(x)
#define tb_assert_pass_break_state(x, s, v)

#define tb_assert_pass_and_check_abort(x) tb_check_abort(x)
#define tb_assert_pass_and_check_return(x) tb_check_return(x)
#define tb_assert_pass_and_check_return_val(x, v) tb_check_return_val(x, v)
#define tb_assert_pass_and_check_goto(x, b) tb_check_goto(x, b)
#define tb_assert_pass_and_check_break(x) tb_check_break(x)
#define tb_assert_pass_and_check_continue(x) tb_check_continue(x)
#define tb_assert_pass_and_check_break_state(x, s, v)                          \
    tb_check_break_state(x, s, v)
#endif

/// assert: noimpl
#define tb_assert_noimpl() tb_assertf(0, "noimpl")

/*! the static assert
 *
 * @code
 *
 * tb_assert_static(sizeof(tb_uint32_t) == 4);
 *
 * @endcode
 */
#if __tb_has_feature__(c_static_assert)
#define tb_assert_static(x) _Static_assert(x, "")
#define tb_assert_static_msg(x, m) _Static_assert(x, m)
#elif defined(__cpp_static_assert)
#define tb_assert_static(x) static_assert(x, "")
#define tb_assert_static_msg(x, m) static_assert(x, m)
#else
#define tb_assert_static(x)                                                    \
    do {                                                                       \
        typedef int __tb_static_assert__[(x) ? 1 : -1];                        \
        __tb_volatile__ __tb_static_assert__ __a = { 0 };                      \
        tb_used_ptr((tb_cpointer_t)(tb_size_t)__a);                            \
    } while (0)
#define tb_assert_static_msg(x, m) tb_assert_static(x)
#endif

    /* //////////////////////////////////////////////////////////////////////////////////////
     * declaration
     */

    /*! dump backtrace
     *
     * @param prefix    the prefix tag
     * @pragm frames    the frame list
     * @param nframe    the frame count
     */
    tb_void_t
    tb_backtrace_dump(
        tb_char_t const *prefix, tb_pointer_t *frames, tb_size_t nframe);

/*! this variable have been used
 *
 * @param variable  the variable
 */
tb_void_t tb_used_ptr(tb_cpointer_t variable);

/* //////////////////////////////////////////////////////////////////////////////////////
 * inlines
 */

#ifdef __tb_debug__
/* the assert implementation
 *
 * @code
 * tb_size_t v = (tb_assert(x), value);
 * tb_assert(v);
 * @endcode
 *
 * @param x                     the boolean value of the expression
 * @param expr                  the expression string
 *
 * @return                      the boolean value of the expression
 */
static __tb_inline__ tb_bool_t tb_assert_impl(
    tb_bool_t x, tb_char_t const *expr __tb_debug_decl__)
{
    // failed?
    if (!x) {
        // trace
        tb_trace_a("expr[%s] at %s(): %d, %s", expr __tb_debug_args__);

        // dump backtrace
        tb_assert_backtrace_dump();

        // sync trace
        tb_trace_sync();

        // abort it
        tb_abort();
    }

    // ok?
    return x;
}
#else
/* the assert empty implementation
 *
 * @return                      only tb_false
 */
static __tb_inline__ tb_bool_t tb_assert_empty_impl(tb_noarg_t)
{
    return tb_false;
}
#endif

__tb_extern_c_leave__

#endif // !TB_PREFIX_ASSERT_H
